package com.zl.registration.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.zl.registration.dao.UserInfoDao;
import com.zl.registration.dao.UserSecurityDao;
import com.zl.registration.vo.RegisterVo;
import com.zl.registration.entity.UserMember;
import com.zl.registration.entity.UserSecurityQuestion;
import com.zl.registration.exception.BusinessException;
import com.zl.registration.mapper.UserMemberMapper;
import com.zl.registration.mapper.UserSecurityQuestionMapper;
import com.zl.registration.module.prefix.UserKeyPrefix;
import com.zl.registration.module.result.CodeMsg;
import com.zl.registration.service.RedisService;
import com.zl.registration.service.UserService;
import com.zl.registration.util.UUIDUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author zoulei
 * @date 2021/11/19 17:14
 */
@Service
public class UserServiceImpl implements UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Resource
    private UserSecurityQuestionMapper userSecurityQuestionMapper;
    @Resource
    private UserMemberMapper userMemberMapper;
    @Resource
    private UserInfoDao userInfoDao;
    @Resource
    private UserSecurityDao userSecurityDao;
    @Resource
    private RedisService redisService;

    @Override
    public CodeMsg register(RegisterVo userModel) {
        // 参数校验
        CodeMsg calibrationResults = parameterVerification(userModel);
        if (null != calibrationResults) {
            return calibrationResults;
        }
        // 加锁
        String uniqueValue = UUIDUtil.uuid() + "-" + Thread.currentThread().getId();
        String lockKey = "redis-lock" + userModel.getPhone();
        boolean lock = redisService.lock(lockKey, uniqueValue, 60 * 1000);
        if (!lock) {
            return CodeMsg.WAIT_REGISTER_DONE;
        }
        logger.debug("注册接口加锁成功");

        // 检查用户是否注册
        UserMember user = this.getUserByPhone(userModel.getPhone());

        // 用户已经注册
        if (null != user) {
            redisService.unlock(lockKey, uniqueValue);
            return CodeMsg.USER_EXIST;
        }

        // 生成user对象
        UserMember newUser = BeanUtil.toBean(userModel, UserMember.class);
        //普通用户
        newUser.setStatus(true);
        newUser.setRegisterTime(DateUtil.date());
        newUser.setLastLoginTime(DateUtil.date());
        // 写入数据库
        int isSuccess = userMemberMapper.insert(newUser);

        UserSecurityQuestion securityQuestion = new UserSecurityQuestion();
        securityQuestion.setPhone(userModel.getPhone())
                .setQuestion(userModel.getQuestion())
                .setAnswer(userModel.getAnswer());
        int insert = userSecurityQuestionMapper.insert(securityQuestion);

        boolean unlock = redisService.unlock(lockKey, uniqueValue);
        if (!unlock) {
            return CodeMsg.REGISTER_FAIL;
        }
        logger.debug("注册接口解锁成功");

        // 用户注册成功
        if (Boolean.TRUE.equals(isSuccess == 1) && Boolean.TRUE.equals(insert == 1)) {
            return CodeMsg.REGISTER_SUCCESS;
        }
        logger.error("isSuccess:{}, insert:{}", isSuccess, insert);
        // 用户注册失败
        return CodeMsg.REGISTER_FAIL;
    }

    @Override
    public String login(String phone, String password) {
        // 判断手机号是否存在(首先从缓存中取，再从数据库取)
        UserMember userByPhone = this.getUserByPhone(phone);
        // 缓存中、数据库中都不存在该用户信息，直接返回
        if (userByPhone == null) {
            throw new BusinessException(CodeMsg.MOBILE_NOT_EXIST);
        }
        logger.info("用户：{}", userByPhone);

        // 判断手机号对应的密码是否一致
        String dbPassword = userByPhone.getPassword();
        if (!password.equals(dbPassword)) {
            throw new BusinessException(CodeMsg.PASSWORD_ERROR);
        }
        // 执行到这里表明登录成功，更新用户cookie
        // 生成cookie
        String token = UUIDUtil.uuid();
        // 每次访问都会生成一个新的session存储于redis和反馈给客户端，一个session对应存储一个user对象
        redisService.set(UserKeyPrefix.TOKEN, token, userByPhone.toString());
        return token;
    }

    /**
     * 根据手机号码获取用户信息
     * 1. 从redis中获取用户数据缓存
     * 2. 如果缓存中没有用户数据，则从数据库中查询数据并将数据写入缓存
     * 先从数据库中取出数据
     * 然后将数据返回并将数据缓存在redis中
     *
     * @param phone 手机号码
     * @return 用户信息
     */
    private UserMember getUserByPhone(String phone) {
        String user = redisService.get(UserKeyPrefix.USER_PHONE, "_" + phone);
        if (user != null) {
            return JSONUtil.toBean(user, UserMember.class);
        }
        UserMember userInfo = userInfoDao.searchByPhone(phone);
        if (userInfo != null) {
            redisService.set(UserKeyPrefix.USER_PHONE, "_" + phone, JSONUtil.toJsonStr(userInfo));
        }
        return userInfo;
    }

    /**
     * 对传入参数进行校验
     *
     * @param registerVo 注册参数
     * @return 校验结果
     */
    private CodeMsg parameterVerification(RegisterVo registerVo) {
        if (BeanUtil.isEmpty(registerVo)) {
            return CodeMsg.PARAMETERS_EMPTY;
        }
        if (BeanUtil.hasNullField(registerVo)) {
            return CodeMsg.PARAMETERS_HAS_EMPTY;
        }
        return null;
    }
}
